/*    Copyright 2010 Tobias Marschall
 *
 *    This file is part of MoSDi.
 *
 *    MoSDi is free software: you can redistribute it and/or modify
 *    it under the terms of the GNU General Public License as published by
 *    the Free Software Foundation, either version 3 of the License, or
 *    (at your option) any later version.
 *
 *    MoSDi is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU General Public License for more details.
 *
 *    You should have received a copy of the GNU General Public License
 *    along with MoSDi.  If not, see <http://www.gnu.org/licenses/>.
 */

package mosdi.util.iterators;

import java.util.Arrays;
import java.util.NoSuchElementException;

import mosdi.fa.Alphabet;
import mosdi.util.Iupac;
import mosdi.util.IupacStringConstraints;

/** Iterator over all strings that match a given IUPAC string. */
public class IupacInstanceIterator implements LexicographicalIterator {
	
	private int[] iupacPattern;
	private IupacPatternIterator iterator;
	private int leftmostChangedPosition;
	private int[] nextPattern;
	private int nextLeftmostChangedPosition;
	
	public IupacInstanceIterator(String iupacPattern) {
		this(Alphabet.getIupacAlphabet().buildIndexArray(iupacPattern));
	}
	
	/** Constructor.
	 * @param iupacPattern Pattern specified w.r.t. Alphabet.getIupacAlphabet().
	 */
	public IupacInstanceIterator(int[] iupacPattern) {
		this.iupacPattern = iupacPattern;
		IupacStringConstraints constraints = new IupacStringConstraints(iupacPattern.length, false);
		this.iterator = new IupacPatternIterator(iupacPattern.length, constraints);
		this.leftmostChangedPosition = -1;
		step();
	}
	
	private void step() {
		nextLeftmostChangedPosition = iupacPattern.length;
		nextPattern = null;
		for (;iterator.hasNext(); nextPattern=null) {
			nextPattern = iterator.next();
			int i = iterator.getLeftmostChangedPosition();
			nextLeftmostChangedPosition = Math.min(nextLeftmostChangedPosition, i);
			for (; i<nextPattern.length; ++i) {
				if (!Iupac.subsetOf(nextPattern[i], iupacPattern[i])) {
					iterator.skip(i);
					break;
				}
			}
			if (i==nextPattern.length) break;
		}
	}
	
	@Override
	public boolean hasNext() {
		return nextPattern!=null;
	}

	@Override
	/** Returns strings over the IUPAC alphabet as given by Alphabet.getIupacAlphabet(). */
	public int[] next() {
		if (nextPattern==null) throw new NoSuchElementException();
		int[] result = nextPattern;
		leftmostChangedPosition = nextLeftmostChangedPosition;
		step();
		return result;
	}

	@Override
	public void remove() {
		throw new UnsupportedOperationException();
	}

	@Override
	public int getLeftmostChangedPosition() {
		return leftmostChangedPosition;
	}

	@Override
	public int getStringLength() {
		return iupacPattern.length;
	}

	@Override
	/** Returns strings over the IUPAC alphabet as given by Alphabet.getIupacAlphabet(). */
	public int[] peek() {
		if (nextPattern==null) throw new NoSuchElementException();
		return Arrays.copyOf(nextPattern, nextPattern.length);
	}

	@Override
	public void skip(int position) {
		iterator.skip(position);
		step();
	}

}
